/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Logger } from '../../framework';

export enum ErrorCode {
  // Common errors (4000xxxx)
  UNKNOWN_ERROR = 40000000,
  DUPLICATE_NAME = 40000001,
  WRONG_DECORATION = 40000002,
  REPEAT_ANNOTATION = 40000003,
  ERROR_CONFIG = 40000004,
  NOT_EMPTY_STRING = 40000005,
  INVALID_STRING_VALUE = 40000006,
  ERROR_PAGE_SOURCE_FILE = 40000007,

  // File operation errors (4000xxxx)
  FILE_ACCESS_ERROR = 40000101,
  FILE_NOT_FOUND = 40000102,
  PARSE_FILE_ERROR = 40000103,

  // Template errors (4000xxxx)
  TEMPLATE_RENDER_ERROR = 40000201,
  TEMPLATE_NOT_FOUND = 40000202,

  // AST parsing errors (4000xxxx)
  UNKNOWN_CLASS = 40000301,
  UNKNOWN_PROPERTY = 40000302,
  UNKNOWN_CONSTANT = 40000303,
  UNKNOWN_VARIABLE = 40000304,

  // Extension errors (4000xxxx)
  EXTENSION_LOAD_ERROR = 40000401,
  EXTENSION_EXECUTE_ERROR = 40000402,
}

const ERROR_MAP: Map<ErrorCode, string> = new Map([
  [ErrorCode.UNKNOWN_ERROR, 'Unknown error'],
  [ErrorCode.DUPLICATE_NAME, 'Duplicate name: %s'],
  [ErrorCode.WRONG_DECORATION, 'Structure with @HMRouter annotation cannot contain NavDestination component, filePath: %s'],
  [ErrorCode.REPEAT_ANNOTATION, 'File contains multiple annotations of the same type: %s'],
  [ErrorCode.ERROR_CONFIG, 'Module context is empty, please check the hvigorfile.ts file in the module directory: %s'],
  [ErrorCode.NOT_EMPTY_STRING, 'Constant value cannot be an empty string: %s'],
  [ErrorCode.INVALID_STRING_VALUE, 'Invalid string value: %s'],
  [ErrorCode.ERROR_PAGE_SOURCE_FILE, 'Page source file is empty, please check the pageSourceFile: %s'],

  [ErrorCode.FILE_ACCESS_ERROR, 'File access error: %s'],
  [ErrorCode.FILE_NOT_FOUND, 'File not found: %s'],
  [ErrorCode.PARSE_FILE_ERROR, 'File parsing error: %s'],

  [ErrorCode.TEMPLATE_RENDER_ERROR, 'Template rendering error: %s'],
  [ErrorCode.TEMPLATE_NOT_FOUND, 'Template not found: %s'],

  [ErrorCode.UNKNOWN_CLASS, 'Unknown class: %s'],
  [ErrorCode.UNKNOWN_PROPERTY, 'Unknown property: %s'],
  [ErrorCode.UNKNOWN_CONSTANT, 'Unknown constant: %s'],
  [ErrorCode.UNKNOWN_VARIABLE, 'Unknown variable: %s'],

  [ErrorCode.EXTENSION_LOAD_ERROR, 'Extension loading error: %s'],
  [ErrorCode.EXTENSION_EXECUTE_ERROR, 'Extension execution error: %s'],
]);

/**
 * Plugin error class
 */
export class PluginError extends Error {
  readonly code: ErrorCode;
  readonly module: string;
  readonly data?: unknown;

  /**
   * Constructor
   * @param code Error code
   * @param module Module name
   * @param params Error parameters
   * @param data Additional data
   */
  constructor(code: ErrorCode, module: string, params?: string | string[], data?: unknown) {
    // Get the error message template
    let message = ERROR_MAP.get(code) || 'Unknown error';
    if (params) {
      if (Array.isArray(params)) {
        params.forEach((param) => {
          message = message.replace(`%s`, param);
        });
      } else {
        message = message.replace('%s', params);
      }
    }

    // Call the parent class constructor
    super(message);

    // Set properties
    this.code = code;
    this.module = module;
    this.data = data;

    // Set the prototype chain correctly
    Object.setPrototypeOf(this, PluginError.prototype);

    // Record the error log
    this.logError();
  }

  private logError(): void {
    Logger.error(this.module, `error code: ${this.code}, error message: ${this.message}`, this.data);
  }

  /**
   * Create an error
   * @param code Error code
   * @param module Module name
   * @param params Error parameters
   * @param data Additional data
   */
  static create(code: ErrorCode, module: string, params?: string | string[], data?: any): PluginError {
    return new PluginError(code, module, params, data);
  }
}
